package com.example.bank.shiro.config;

import com.example.bank.domain.ShiroRealm;
import com.example.bank.filter.JWTFilter;
import com.example.bank.shiro.realm.JWTRealm;
import com.example.bank.util.JWTCredentialsMatcher;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authc.pam.AuthenticationStrategy;
import org.apache.shiro.authc.pam.FirstSuccessfulStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.authz.Authorizer;
import org.apache.shiro.authz.ModularRealmAuthorizer;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.mgt.SessionStorageEvaluator;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;

/**
 * @author ：Leeziqiang
 * @description：TODO
 * @date ：2021/11/21 17:09
 */
@Configuration
public class ShiroConfig {

    // 交由spring来自动管理shiro-bean的生命周期
    @Bean
    public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    // 为spring-baen开启对shiro注解的支持
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator app = new DefaultAdvisorAutoProxyCreator();
        app.setProxyTargetClass(true);
        return app;
    }

    // 不向spring容器中注册JwtFilter bean，防止Spring将JWTFilter注册为全局过滤器。
    // 全局过滤器会对所有请求进行拦截，而本例中只需要拦截除/login和/logout之外的请求
    // 另外一种简单的做法是：直接去掉jwtFilter（）上的@Bean注解
    @Bean
    public FilterRegistrationBean<Filter> registration(JWTFilter jwtFilter) {
        FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<Filter>(jwtFilter);
        registrationBean.setEnabled(false);
        return registrationBean;
    }

    @Bean
    public JWTFilter jwtFilter() {
        // 过滤器如果加了@Component注解就不必实现这个方法
        return new JWTFilter();
    }

    // 配置访问资源需要的权限
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 给工厂bean设置web安全管理器
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        shiroFilterFactoryBean.setLoginUrl("/user/register");
        shiroFilterFactoryBean.setSuccessUrl("/authorized");
        shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");

        // 添加jwt专用过滤器，拦截除/login /logout之外的请求
        LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        // 可匿名访问
        filterChainDefinitionMap.put("/back/**","anon");
        filterChainDefinitionMap.put("/wx","anon");
        filterChainDefinitionMap.put("/check","anon");
        filterChainDefinitionMap.put("/interface", "anon");
        filterChainDefinitionMap.put("/user/login","anon");
        filterChainDefinitionMap.put("/user/admin","anon");
        // 需要登录才能访问
        filterChainDefinitionMap.put("/client/**", "jwtFilter,authc");
        filterChainDefinitionMap.put("/user/list", "jwtFilter,authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    // 配置 ModularRealmAuthenticator
    @Bean
    public ModularRealmAuthenticator authenticator() {
        ModularRealmAuthenticator authenticator = new ModularRealmAuthenticator();
        // 设置多Realm的认证策略， 默认 AtLeastOneSuccessfulStrategy
        AuthenticationStrategy strategy = new FirstSuccessfulStrategy();
        authenticator.setAuthenticationStrategy(strategy);
        return authenticator;
    }

    // 禁用session，不保存用户登录状态，保证每次请求都重新认证
//    @Bean
//    protected SessionStorageEvaluator sessionStorageEvaluator() {
//        DefaultSessionStorageEvaluator sessionStorageEvaluator = new DefaultSessionStorageEvaluator();
//        sessionStorageEvaluator.setSessionStorageEnabled(false);
//        return sessionStorageEvaluator;
//    }

    // JWTRealm 配置，需实现Realm接口
    // TODO
    @Bean
    JWTRealm jwtRealm() {
        JWTRealm jwtRealm = new JWTRealm();
        // 设置加密算法
        CredentialsMatcher credentialsMatcher = new JWTCredentialsMatcher();
        // 设置加密次数
        jwtRealm.setCredentialsMatcher(credentialsMatcher);
        return jwtRealm;
    }

    // ShiroRealm 配置，需实现Realm接口
    @Bean
    ShiroRealm shiroRealm() {
        // 这里其实是模拟的数据库的类， 但是也继承了AuthorizingRealm
        ShiroRealm shiroRealm = new ShiroRealm();
        // 设置加密算法
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher("SHA-1");
        // 设置加密次数
        credentialsMatcher.setHashIterations(16);
        shiroRealm.setCredentialsMatcher(credentialsMatcher);
        return shiroRealm;
    }

    // 配置DefaultWebSecurityManager
    @Bean
    public DefaultWebSecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

        // 1.Authenticator
        securityManager.setAuthenticator(authenticator());
        // 2.Realm
        List<Realm> realms = new ArrayList<Realm>(16);
        realms.add(jwtRealm());
        realms.add(shiroRealm());
        // 配置多个Realm
        securityManager.setRealms(realms);

//        // 关闭shiro自带的session
//        DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
//        subjectDAO.setSessionStorageEvaluator(sessionStorageEvaluator());
//        securityManager.setSubjectDAO(subjectDAO);

        return securityManager;
    }

    @Bean
    public Authorizer authorizer() {
        // 如果没有这个bean启动会报错
        return new ModularRealmAuthorizer();
    }
}
